For this project, I use NYC Open Data to alleviate my anxieties about living on the top floor of an apartment building and investigate serious incidents requiring the fire department to respond. Using data about the locations of Fire Stations and fires occurring in New York City, I look into whether response times to fires differ across the city. Lastly, I focus on whether the distance from the Fire Station affects response times to the scene.
NYC Open Data has data on all incidents responded to by fire companies. I have included the variable description file in the exercise folder. The following variables are available:
This dataset is only updated annually, and thus far only data from 2013 to 2015 is contained. The following analyses are run using just 2015 data and only the most severe fires (Level 7).
# data manipulation (by Thomas Brambor)
fire <- read_csv("./data/Incidents_Responded_to_by_Fire_Companies.csv")
fire$year <- substr(fire$INCIDENT_DATE_TIME, 7, 10)
fire <- fire%>%
filter(HIGHEST_LEVEL_DESC == "7 - Signal 7-5") %>%
filter(year==2015)
# Geocoding data
# Make list of addresses
address <- str_c( str_to_title(fire$STREET_HIGHWAY),
"New York, NY",
fire$ZIP_CODE,
sep=", ")
# Register Google API Key
register_google(key = Sys.getenv("GOOGLE_MAPS_API_KEY"))
# Geocode Addresses
latlong <- geocode(address, output = c("latlon"))
# Merge on
fire$Latitude <- latlong$lat
fire$Longitude <- latlong$lon
# Save File
write_csv(fire, "severe_incidents.csv")NYC Open Data also provides data on the location of all 218 Fire Stations in NYC. Relevant for our analyses are the following variables:
FacilityName, Borough, Latitude, Longitude
# Read in both datasets
library(tidyverse)
fire <- read_csv("../data/severe_incidents.csv")
locs <- read_csv("../data/FDNY_Firehouse_Listing.csv")
locs <- locs %>%
select(FacilityName, Borough, Latitude, Longitude)Below is a leaflet map of the severe fires contained in the file severe_incidents.csv. Fires that occurred at locations outside the five boroughs of New York City were excluded. The type of incident, date and time of incident, where the fire spread, the number of firemen/women units that came onto scene, whether a detector was present and the total duration of the incident are available as information in interactive popups.
library(leaflet)
library(RColorBrewer)
# popups
fire$INCIDENT_TYPE_DESC <- substring(fire$INCIDENT_TYPE_DESC, 7) # rid of numbers
fire$FIRE_SPREAD_DESC <- substring(fire$FIRE_SPREAD_DESC, 5)
fire$DETECTOR_PRESENCE_DESC <- substring(fire$DETECTOR_PRESENCE_DESC, 5)
fire$TOTAL_INCIDENT_DURATION <- round(fire$TOTAL_INCIDENT_DURATION/60, 2) # convert to minutes
content <- paste("What:",fire$INCIDENT_TYPE_DESC,"<br/>",
"When:",fire$INCIDENT_DATE_TIME,"<br/>",
"Location:",fire$FIRE_SPREAD_DESC,"<br/>",
"Length of Incident (min):", fire$TOTAL_INCIDENT_DURATION, "<br/>",
"Number of Units On Scene:",fire$UNITS_ONSCENE,"<br/>",
"Fire Dectector Status:", fire$DETECTOR_PRESENCE_DESC, "<br/>")
fireIcons <- icons(
iconUrl = "../images/fire.png",
iconWidth = 8, iconHeight = 11
#iconAnchorX = 22, iconAnchorY = 94
)
# map
leaflet(data = fire, options = leafletOptions(minZoom = 10)) %>%
setView(lat = 40.730610, lng = -73.935242, zoom = 10) %>%
addTiles() %>%
addProviderTiles("CartoDB.Positron") %>%
addMarkers(lat = ~Latitude, lng = ~Longitude,
icon = fireIcons, popup = content)Out of curiosity, I created a heatmap of the same most severe incidents to investigate whether certain areas in NYC had more 2015 fires than others. From the map we can see Manhattan, the Bronx, and some of Brooklyn experienced the most severe incidents while Queens and Long Island didn’t experience quite as many. This is likely due to population density differences between these boroughs.
incident_map <- get_map(location = c(lon = -73.935242, lat = 40.730610), maptype = "toner-lite", zoom = 10)
ggmap(incident_map) +
geom_density2d(aes(x = Longitude, y = Latitude),
data = fire,
color="blue",
size=0.5,
bins=10) +
stat_density2d(aes(x = Longitude, y = Latitude,
fill = ..level..,
alpha = ..level..),
data = fire,
geom = 'polygon',
bins = 10,
show.legend = FALSE) +
scale_fill_gradient2(low = "yellow", mid = "orange", high = "red") +
scale_alpha(range = c(0.00, 0.5)) +
labs(x = '', y = '') +
theme(axis.text = element_blank()) +
theme(axis.ticks = element_blank())Beginning with the previous leaflet map, I distinguished the markers of the fire locations by PROPERTY_USE_DESC, i.e. what kind of property was affected. There were many categories of properties, so I rounded them down as much as possible while still providing interesting information. Information about the property type as well the same details about the incidents are recorded in interactive popups. AND I changed the map to black for dramatic effect.
### Data Cleaning
fire$PROPERTY_USE_DESC <- substring(fire$PROPERTY_USE_DESC, 7) # rid of numbers before prop name
tab <- as.data.frame(table(fire$PROPERTY_USE_DESC)) # table to get frequency
colnames(tab) <- c("PROPERTY_USE_DESC", "Freq")
fireprop <- merge(x = fire, y = tab, by = "PROPERTY_USE_DESC", all = TRUE) # merge df and table
fireprop$PROPERTY_USE_DESC <- ifelse(fireprop$Freq < 3, "Other", fireprop$PROPERTY_USE_DESC) # all property types with fewer than 10 occurences become "other"
## Renaming and Classifying
# Open spaces
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Open land or field"] <- "Open Land/Field"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Outside or special property, other"] <- "Open Land/Field"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Dock, marina, pier, wharf"] <- "Open Land/Field"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Forest, timberland, woodland"] <- "Open Land/Field"
# schools
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Schools, non-adult, other"] <- "School"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Adult education center, college classroom"] <- "School"
# residential spaces
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Residential, other"] <- "Residential Home"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Multifamily dwelling"] <- "Residential Home"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="1 or 2 family dwelling"] <- "Residential Home"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Dormitory-type residence, other"] <- "Residential Home"
# streets
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Street, other"] <- "Street/Highway"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Residential street, road or residential driveway"] <- "Street/Highway"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Street or road in commercial area"] <- "Street/Highway"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Railroad right-of-way"] <- "Street/Highway"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Highway or divided highway"] <- "Street/Highway"
# parking garages
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Parking garage, (detached residential garage)"] <- "Parking Garage"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Vehicle parking area"] <- "Parking Garage"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Vehicle storage, other"] <- "Parking Garage"
# hotels
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Hotel/motel, commercial"] <- "Hotel/Motel"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Boarding/rooming house, residential hotels"] <- "Hotel/Motel"
# businesses
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Mercantile, business, other"] <- "Mercantile Business"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Laundry, dry cleaning"] <- "Mercantile Business"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Business office"] <- "Business Office"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="General retail, other"] <- "Mercantile Business"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Specialty shop"] <- "Mercantile Business"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Professional supplies, services"] <- "Mercantile Business"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Food and beverage sales, grocery store"] <- "Mercantile Business"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Convenience store"] <- "Mercantile Business"
# restaurants
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Restaurant or cafeteria"] <- "Restaurant/Bar"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Eating, drinking places, other"] <- "Restaurant/Bar"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Bar or nightclub"] <- "Restaurant/Bar"
# warehouses
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Property Use, other"] <- "Warehouse/Storage"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Storage, other"] <- "Warehouse/Storage"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Warehouse"] <- "Warehouse/Storage"
# others
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Hospital - medical or psychiatric"] <- "Hospital"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Motor vehicle or boat sales, services, repair"] <- "Gas Station/Vehicle Repair"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Service station, gas station"] <- "Gas Station/Vehicle Repair"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Rapid transit station"] <- "Transit Station"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Manufacturing, processing"] <- "Other"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Outbuilding or shed"] <- "Other"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Undetermined"] <- "Other"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Industrial plant yard - area"] <- "Other"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Church, mosque, synagogue, temple, chapel"] <- "Other"
fireprop$PROPERTY_USE_DESC[fireprop$PROPERTY_USE_DESC=="Construction site"] <- "Other"
### Map
# create order of factor based on how many incidents occured in that type of property
fireprop$PROPERTY_USE_DESC <- factor((fireprop$PROPERTY_USE_DESC), levels = c("Hospital", "Transit Station", "School", "Hotel/Motel", "Gas Station/Vehicle Repair", "Warehouse/Storage", "Parking Garage", "Business Office", "Restaurant/Bar", "Open Land/Field", "Street/Highway", "Other", "Mercantile Business", "Residential Home"))
#colors <- c("#08519c", "#3182bd", "#6baed6", "#9ecae1", "#c6dbef", "#ffffcc", "#ffeda0", "#fed976", #"#feb24c", "#fd8d3c", "#fc4e2a", "#e31a1c", "#bd0026", "#800026")
colorCount <- length(unique(fireprop$PROPERTY_USE_DESC)) # number of levels
colors = colorRampPalette(brewer.pal(9, "YlOrRd"))(colorCount)
# popups
content <- paste("What:",fireprop$INCIDENT_TYPE_DESC,"<br/>",
"When:",fireprop$INCIDENT_DATE_TIME,"<br/>",
"Where:",fireprop$PROPERTY_USE_DESC,"<br/>", # added where
"Location:",fireprop$FIRE_SPREAD_DESC,"<br/>",
"Length of Incident (min):", fireprop$TOTAL_INCIDENT_DURATION, "<br/>",
"Number of Units On Scene:",fireprop$UNITS_ONSCENE,"<br/>",
"Fire Dectector Status:", fireprop$DETECTOR_PRESENCE_DESC, "<br/>")
# map -- starting with other map but changed data source
leaflet(data = fireprop, options = leafletOptions(minZoom = 9)) %>%
setView(lat = 40.730610, lng = -73.935242, zoom = 9) %>%
addTiles() %>%
addProviderTiles("CartoDB.DarkMatter") %>%
addCircles(lat = ~Latitude, lng = ~Longitude,
color = colors,
weight = 5,
opacity = 0.7,
fill = TRUE,
fillOpacity = 0.5,
popup = content ) %>%
addLegend("bottomright",
colors = c("#FFFFCC", "#FFF3B0", "#FEE896", "#FEDC7C", "#FEC662", "#FDAF4A", "#FD9840", "#FC7936", "#FC522B", "#EE3222", "#DD151D", "#C50523","#A50026","#800026"),
values = ~fireprop$PROPERTY_USE_DESC,
title = "Incident Property Type",
labels = c("Hospital", "Transit Station", "School", "Hotel/Motel", "Gas Station/Vehicle Repair", "Warehouse/Storage", "Parking Garage", "Business Office", "Restaurant/Bar", "Open Land/Field", "Street/Highway", "Other", "Mercantile Business", "Residential Home"))#colors = c("#08519c", "#3182bd", "#6baed6", "#9ecae1", "#c6dbef", "#ffffcc", "#ffeda0", "#fed976", #"#feb24c", "#fd8d3c", "#fc4e2a", "#e31a1c", "#bd0026", "#800026") # blue/redI added marker clustering, so that zooming in reveals the individual locations but the zoomed out map only shows the clusters.
leaflet(data = fireprop, options = leafletOptions(minZoom = 10)) %>%
setView(lat = 40.730610, lng = -73.935242, zoom = 10) %>%
addTiles() %>%
addProviderTiles("CartoDB.DarkMatter") %>%
addCircleMarkers(lat = ~Latitude, lng = ~Longitude,
color = colors,
popup = content,
clusterOptions = markerClusterOptions()) %>%
addLegend("bottomright",
colors = c("#FFFFCC", "#FFF3B0", "#FEE896", "#FEDC7C", "#FEC662", "#FDAF4A", "#FD9840", "#FC7936", "#FC522B", "#EE3222", "#DD151D", "#C50523","#A50026","#800026"),
values = ~fireprop$PROPERTY_USE_DESC,
title = "Incident Property Type",
labels = c("Hospital", "Transit Station", "School", "Hotel/Motel", "Gas Station/Vehicle Repair", "Warehouse/Storage", "Parking Garage", "Business Office", "Restaurant/Bar", "Open Land/Field", "Street/Highway", "Other", "Mercantile Business", "Residential Home"))Next, I adjusted the initial leaflet map such that the size of the circle markers indicate severity (by the number of units that came on to the scene) and colors indicated the length of time the fire persisted. Fire station locations were also added. There are two layers to the map (“Incidents”, “Firehouses”) that allow the user to select which information to show.
# colors
fireprop$brks <- cut(fireprop$TOTAL_INCIDENT_DURATION,
breaks=c(0, 75, 110, 120, 150, 7140),
labels=c("10-75 min", "1.25-1.8 hrs", "1.8-2 hrs",
"2-2.5 hrs", ">2.5 hrs"))
bins <- c(0, 75, 110, 120, 150, 7140)
pal <- colorBin("YlOrRd", domain = fireprop$TOTAL_INCIDENT_DURATION, bins = bins)
color <- pal(fireprop$TOTAL_INCIDENT_DURATION)
# icon
firehouseIcons <- icons(
iconUrl = "http://icons.iconarchive.com/icons/icons8/windows-8/48/Maps-Marker-icon.png",
iconWidth = 15, iconHeight = 20
)
# popup content
# popups
content <- paste("What:",fireprop$INCIDENT_TYPE_DESC,"<br/>",
"When:",fireprop$INCIDENT_DATE_TIME,"<br/>",
"Where:",fireprop$PROPERTY_USE_DESC,"<br/>", # added where
"Location:",fireprop$FIRE_SPREAD_DESC,"<br/>",
"Length of Incident:", fireprop$brks, "<br/>",
"Number of Units On Scene:",fireprop$UNITS_ONSCENE,"<br/>",
"Fire Dectector Status:", fireprop$DETECTOR_PRESENCE_DESC, "<br/>")
# new content for firehouse popups
loc_content <- paste("Facility Name:",locs$FacilityName,"<br/>",
"Borough:", locs$Borough, "<br/>")
# map
leaflet(options = leafletOptions(minZoom = 8)) %>%
setView(lat = 40.730610, lng = -73.935242, zoom = 10) %>%
addTiles() %>%
addProviderTiles("CartoDB.Positron", group = "New York City") %>%
addCircleMarkers(data = fireprop,
lat = ~Latitude, lng = ~Longitude,
color = color,
opacity = 0.8,
fillOpacity = 0.7,
weight = 1,
radius = ~fireprop$UNITS_ONSCENE/2, # make radius smaller
popup = content,
group = "Incidents") %>%
addMarkers(data = locs,
lat = ~Latitude, lng = ~Longitude,
popup = loc_content,
icon = firehouseIcons,
group = "Fire Stations") %>%
addLegend("bottomright",
colors = c("#FFFFB2", "#FECC5C", "#FD8D3C", "#F03B20", "#BD0026"),
values = ~fireprop$brks,
title = "Length of Incident",
labels = c("10-75 min", "1.25-1.8 hrs", "1.8-2 hrs",
"2-2.5 hrs", ">2.5 hrs")) %>%
addLayersControl(
baseGroups = "New York City",
overlayGroups = c("Incidents", "Fire Stations"))I then investigated whether the distance of the incident from the nearest Fire Station varied across the city. For all incident locations, I identified the nearest Fire Station and calculated the distance between the Fire Station and the incident location. Below is a look at the dataframe generated with the fire station deemed closest to the incident and the calculated approximate distance (mi).
library(geosphere)
# Fire Station locations lat, lon
fh_loc <- locs[ , 3:4]
colnames(fh_loc) <- c("lat", "lon")
fh_loc <- na.omit(fh_loc)
# incident locations lat, lon
inc_loc <- fire[ , c(25:26,1)]
colnames(inc_loc) <- c("lat", "lon", "IM_INCIDENT_KEY")
inc_loc <- na.omit(inc_loc)
# calculating distance between incident and nearest Fire Station
# break up into chunks of 213 due to issues with dfs being different sizes
set1 <- inc_loc[1:213, ]
set2 <- inc_loc[214:426, ]
set3 <- inc_loc[427:639, ]
set4 <- inc_loc[640:852, ]
set5 <- inc_loc[853:1065, ]
set6 <- inc_loc[1066:1278, ]
set7 <- inc_loc[1279:1491, ]
set8 <- inc_loc[1492:1704, ]
set9 <- inc_loc[1705:1917, ]
set10 <- inc_loc[1918:2130, ]
set11 <- rbind(inc_loc[2131:2319, ], inc_loc[1:24,])
sets <- list(set1, set2, set3, set4, set5, set6, set7, set8, set9, set10, set11)
df <- NULL
for (i in sets) {
incidents <- i
i$nearest_fh_ID <- which.min(distGeo(incidents, fh_loc))
i$distance <- round((min(distGeo(incidents, fh_loc)) * 0.000621371), 2) # convert to miles
df<-rbind(df,i)
}
firehouse_incidents <- df[-(2319:2342),] # rid of dummy data
rownames(firehouse_incidents) <- 1:nrow(firehouse_incidents) # reset index
# time until firetruck arrived
full_fire <- merge(x = firehouse_incidents, y = fireprop, by = "IM_INCIDENT_KEY", all = TRUE)
full_fire$INCIDENT_DATE_TIME <- strptime(full_fire$INCIDENT_DATE_TIME, "%m/%d/%Y %I:%M:%S %p")
full_fire$ARRIVAL_DATE_TIME <- strptime(full_fire$ARRIVAL_DATE_TIME, "%m/%d/%Y %I:%M:%S %p")
full_fire$diff_time <- as.numeric(difftime(full_fire$ARRIVAL_DATE_TIME,
full_fire$INCIDENT_DATE_TIME,
tz = "EST",
units = "mins"))
full_fire <- merge(x = locs, y = full_fire, by.x = 0, by.y = "nearest_fh_ID", all = TRUE)
head(full_fire %>%
select(IM_INCIDENT_KEY, Borough, FacilityName, distance))## IM_INCIDENT_KEY Borough FacilityName distance
## 1 59025402 Manhattan Engine 4/Ladder 15 0.26
## 2 59020429 Manhattan Engine 4/Ladder 15 0.26
## 3 59023604 Manhattan Engine 4/Ladder 15 0.26
## 4 59025746 Manhattan Engine 4/Ladder 15 0.26
## 5 59029093 Manhattan Engine 4/Ladder 15 0.26
## 6 59025835 Manhattan Engine 4/Ladder 15 0.26
First, I visualized a simple scatter plot to see the general distribution of responsiveness. Overall, incidents are responded to quite quickly in NYC with most firefighters arriving at the scene within 10 minutes. I noticed one extreme outlier (an incident that took over an hour to arrive to in an open field) so eliminated that one point for more meaningful insights.
library(ggthemes)
ggplot(full_fire[!(full_fire$diff_time > 40), ], aes(x = IM_INCIDENT_KEY, y = diff_time)) +
geom_point(aes(color = diff_time)) +
scale_color_gradientn(colors = rev(brewer.pal(4,"YlOrRd"))) +
labs(y = "Arrival Time (min)",
x = "Fire Incidents",
title = "NYC Fire Responders: 2015",
subtitle = "Arrival times quick & relatively stable") +
#coord_flip() +
theme_tufte() +
theme(axis.text.x = element_blank()) +
theme(axis.ticks.x = element_blank()) +
theme(legend.position = "none")Next, I looked at whether arrival times differed depending on the property type. I thought units would be more responsive to homes/residences in general, but it turns out there’s not much of a difference in how quickly rescuers arrive based on property type. There were a few more incidences of rescuers arriving to fires in open fields and highways a bit later than they would at a residence or restaurant – though, based on this scatter plot, a restaurant/bar would be the place to be in a fire in terms of reliability of firefighters arrivals.
#colors = colorRampPalette(brewer.pal(9, "YlOrRd"))(colorCount)
ggplot(data = full_fire[!(full_fire$diff_time > 40|is.na(full_fire$PROPERTY_USE_DESC)), ], aes(x = reorder(PROPERTY_USE_DESC, diff_time), y = diff_time)) +
geom_point(aes(color = diff_time)) +
scale_color_gradientn(colors = rev(brewer.pal(3,"YlOrRd"))) +
#scale_color_manual(values=c("#FFFFCC", "#FFF3B0", "#FEE896", "#FEDC7C", "#FEC662", "#FDAF4A", "#FD9840", #"#FC7936", "#FC522B", "#EE3222", "#DD151D", "#C50523", "#A50026", "#800026")) +
labs(y = "Arrival Time (min)", x = '',
title = "NYC Fire Responders: 2015",
subtitle = "Arrival times to incidents by property type") +
coord_flip() +
theme_tufte() +
theme(legend.position = "none")Next, I visualized firefighter arrival times to incidents by NYC borough. Again, there’s not too much variation between the boroughs but there are a few incidents in Staten Island, Queens and Manhattan that were long waits for help.
full_fire$BOROUGH_DESC <-substring(full_fire$BOROUGH_DESC, 5)
ggplot(full_fire[!(full_fire$diff_time > 40|is.na(full_fire$BOROUGH_DESC)), ], aes(x = as.factor(BOROUGH_DESC), y = diff_time)) +
geom_point(aes(color = diff_time)) +
scale_color_gradientn(colors = rev(brewer.pal(3,"YlOrRd"))) +
labs(y = "Arrival Time (min)", x = "",
title = "NYC Fire Responders: 2015",
subtitle = "Arrival times to incidents by NYC borough: Bronx the place to be") +
coord_flip() +
theme_tufte() +
theme(legend.position = "none")I wanted to see whether distance from one’s nearest Fire Station mattered in terms of how quickly responders arrive to the scene. Oddly, it doesn’t seem like distance to a fire station matters in terms of how fast help will arrive. Luckily, in NYC nobody is more than 0.65 miles away from a fire station, so I wonder if I’d see a larger difference in a city/suburb with fewer stations.
ggplot(full_fire[!(full_fire$diff_time > 40|is.na(full_fire$distance)), ], aes(x = distance, y = diff_time)) +
geom_point(aes(color = diff_time)) +
scale_color_gradientn(colors = rev(brewer.pal(3,"YlOrRd"))) +
labs(y = "Arrival Time (min)",
x = "Distance from Nearest Fire Station (mi)",
title = "NYC Fire Responders: 2015",
subtitle = "Arrival times quick & stable regardless of distance to incident") +
#coord_flip() +
theme_tufte() +
theme(legend.position = "none")Lastly, I created a map visualization of response times to the incidents. I differentiated by the incident’s distance from the nearest Fire Station and colored each property by the duration of the incident. The maps make it clear that there are far fewer locations that are even more than a half mile away from a Fire Station as opposed to less than half a mile (and even more properties are within a quarter mile from a Fire Station!). This relieved my anxieties more than anything else, but, nothing too interesting is gleaned from this visualization. Length of a fire must depend on other aspects I didn’t have access to in this dataset.
# categories of distance to fire station
full_fire$distance_rd <- ifelse(full_fire$distance < .25,
"Closer Than Quarter Mile",
full_fire$distance)
full_fire$distance_rd <- ifelse(full_fire$distance >= .25 & full_fire$distance < .50,
"Between Quarter & Half Mile",
full_fire$distance_rd)
full_fire$distance_rd <- ifelse(full_fire$distance >= .5,
"Further Than Half Mile",
full_fire$distance_rd)
# breaks in fire length
full_fire$brks <- cut(full_fire$TOTAL_INCIDENT_DURATION,
breaks=c(0, 75, 110, 120, 150, 7140),
labels=c("10-75 min", "1.25-1.8 hrs", "1.8-2 hrs",
"2-2.5 hrs", ">2.5 hrs"))
# map
ggmap(incident_map) +
geom_point(data = full_fire[!is.na(full_fire$distance_rd), ],
aes(x = lon, y = lat, color = brks),
alpha = 0.5,
size = 1) +
scale_color_brewer(name = "Length of Fire:",
type = "seq",
palette = "YlOrRd",
direction = 1,
guide = "legend") +
labs(x = '', y = '',
title = "NYC Fire Incidents & Proximity to Nearest Fire Station",
subtitle = "2015") +
theme_tufte() +
theme(axis.text = element_blank()) +
theme(axis.ticks = element_blank()) +
facet_wrap(~distance_rd, nrow = 1, drop = TRUE) +
theme(strip.background = element_rect(fill = "#ffff99")) +
theme(legend.position = "right")